package org.anno.chunkdb;

import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Singleton;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Shows table listing, edit, or view.
 */
@Singleton
public class TableServlet extends HttpServlet {

  private final TableDao dao;

  @VisibleForTesting
  public TableServlet(TableDao dao) {
    this.dao = dao;
  }

  public TableServlet() {
    this(new TableDao());
  }

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    resp.setContentType("text/html");
    String uri = req.getRequestURI();
    int i = uri.lastIndexOf('/');
    if (i <= 0 || i == uri.length() - 1) {
      showListing(req, resp);
    } else {
      String id = uri.substring(i + 1);
      Metadata.Table table;
      try {
        table = dao.get(id);
      } catch (EntityNotFoundException e) {
        log("Not found", e);
        resp.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
      }
      if ("edit".equals(req.getParameter("action"))) {
        showEditView(table, req, resp);
      } else {
        showDetailView(table, req, resp);
      }
    }
  }

  private void showDetailView(final Metadata.Table table, final HttpServletRequest req,
                              HttpServletResponse resp) throws IOException {
    String tableId = table.getTableId();
    int limit = getIntParameter(req, "limit", 100);
    final int orderByColumn = getColumnParameter(req, "orderBy", table, -1);
    boolean ascending = getBooleanParameter(req, "ascending", true);
    PlanFactory.Builder builder = orderByColumn < 0 ? getScanPlanFactoryBuilder(table)
        : getIndexScanPlanFactoryBuilder(table, orderByColumn, ascending);
    final PlanFactory planFactory = builder.limit(limit).build();
    OutputStream os = resp.getOutputStream();
    OutputStreamWriter writer = new OutputStreamWriter(os);
    final ResponseVisitor.Html htmlVisitor = new ResponseVisitor.Html(os, orderByColumn, ascending);
    new Page(writer, table.getName()) {
      @Override
      protected void renderHeader() throws IOException {
        out.append("<h2>").append(table.getName()).append("</h2>\n");
        out.append("<h3><a href='").append(table.getAttributionLink()).append("'>")
            .append(table.getAttribution()).append("</a></h3>");
      }

      @Override
      protected void renderContent() throws IOException {
        for (byte[] chunk : dao.getChunks(tableId)) {
          planFactory.getPlan(chunk).accept(htmlVisitor);
        }
      }
    }.render();

  }

  private PlanFactory.Builder getIndexScanPlanFactoryBuilder(Metadata.Table table,
      int orderByColumn, boolean ascending) {
    return () -> chunk -> new IndexScanPlan(table, orderByColumn, chunk, ascending);
  }

  private PlanFactory.Builder getScanPlanFactoryBuilder(Metadata.Table table) {
    return () -> chunk -> new ChunkScanPlan(table, chunk);
  }

  private int getColumnParameter(HttpServletRequest req, String name, Metadata.Table table,
                                 int defaultValue) {
    String parameter = req.getParameter(name);
    if (parameter == null) {
      return defaultValue;
    }
    try {
      return Integer.parseInt(parameter);
    } catch (NumberFormatException ignore) {
    }
    for (Metadata.Column column : table.getColumnList()) {
      if (parameter.equals(column.getName())) {
        return column.getColumnId();
      }
    }
    return defaultValue;
  }

  private int getIntParameter(HttpServletRequest req, String name, int defaultValue) {
    String parameter = req.getParameter(name);
    if (parameter != null) {
      try {
        return Integer.parseInt(parameter);
      } catch (NumberFormatException ignore) {
      }
    }
    return defaultValue;
  }

  private boolean getBooleanParameter(HttpServletRequest req, String name, boolean defaultValue) {
    String parameter = req.getParameter(name);
    if (parameter != null) {
      try {
        return Boolean.parseBoolean(parameter);
      } catch (NumberFormatException ignore) {
      }
    }
    return defaultValue;
  }

  private void showEditView(Metadata.Table table, HttpServletRequest req,
                            HttpServletResponse resp) {

  }

  private void showListing(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    new Page(resp.getWriter(), "Table Listing") {
      protected void renderHeader() throws IOException {
        out.append("<h2>Table Listing</h2>\n");
      }

      @Override
      protected void renderContent() throws IOException {
        out.append("<table class='pure-table pure-table-striped'><thead>"
                   + "<tr><th>Name</th><th>Description</th><th>Attribution</th></tr>"
                   + "</thead><tbody>");
        for (Metadata.Table table : dao.listTables()) {
           out.append("<tr><td><a href='").append(table.getTableId()).append("'>")
               .append(table.getName()).append("</a></td><td>")
               .append(table.getDescription()).append("</td><td><a href='")
               .append(table.getAttributionLink()).append("'>").append(table.getAttribution())
               .append("</a></td></tr>");
        }
        out.append("</tbody></table>");
      }
    }.render();
  }

}
